#pragma once

/**
 * @file opt_logging.h
 * @brief 优化的日志系统实现
 * @details 提供高性能、无锁的日志记录功能，与logging.h相比增加了字符串类型的直接支持
 */

#include <string>
#include <fstream>
#include <cstdio>

#include "macros.h"
#include "lf_queue.h"
#include "thread_utils.h"
#include "time_utils.h"

namespace OptCommon {
  /**
   * @namespace OptCommon
   * @brief 包含优化版工具类和函数的命名空间
   */
  /**
   * @brief 日志队列的最大大小
   * @details 无锁队列用于存储待写入的日志数据，大小为8MB
   */
  constexpr size_t LOG_QUEUE_SIZE = 8 * 1024 * 1024;

  /**
   * @brief 日志元素的类型枚举
   * @details 定义了日志元素可能的数据类型，包括字符串类型
   */
  enum class LogType : int8_t {
    CHAR = 0,
    INTEGER = 1,
    LONG_INTEGER = 2,
    LONG_LONG_INTEGER = 3,
    UNSIGNED_INTEGER = 4,
    UNSIGNED_LONG_INTEGER = 5,
    UNSIGNED_LONG_LONG_INTEGER = 6,
    FLOAT = 7,
    DOUBLE = 8,
    STRING = 9
  };

  /**
   * @brief 表示单个原始日志条目
   * @details 使用联合体存储不同类型的日志数据，包括字符串类型
   */
  struct LogElement {
    LogType type_ = LogType::CHAR;
    union {
      char c;
      int i;
      long l;
      long long ll;
      unsigned u;
      unsigned long ul;
      unsigned long long ull;
      float f;
      double d;
      char s[256];
    } u_;
  };

  /**
   * @brief 优化的日志记录器类
   * @details 提供高性能的日志记录功能，使用后台线程写入日志文件，并支持直接记录字符串
   */
  class OptLogger final {
  public:
    /**
     * @brief 消费日志队列并写入日志文件
     * @details 后台线程执行的函数，从无锁队列中读取日志条目并写入文件
     */
    auto flushQueue() noexcept {
      while (running_) {

        for (auto next = queue_.getNextToRead(); queue_.size() && next; next = queue_.getNextToRead()) {
          switch (next->type_) {
            case LogType::CHAR:
              file_ << next->u_.c;
              break;
            case LogType::INTEGER:
              file_ << next->u_.i;
              break;
            case LogType::LONG_INTEGER:
              file_ << next->u_.l;
              break;
            case LogType::LONG_LONG_INTEGER:
              file_ << next->u_.ll;
              break;
            case LogType::UNSIGNED_INTEGER:
              file_ << next->u_.u;
              break;
            case LogType::UNSIGNED_LONG_INTEGER:
              file_ << next->u_.ul;
              break;
            case LogType::UNSIGNED_LONG_LONG_INTEGER:
              file_ << next->u_.ull;
              break;
            case LogType::FLOAT:
              file_ << next->u_.f;
              break;
            case LogType::DOUBLE:
              file_ << next->u_.d;
              break;
            case LogType::STRING:
              file_ << next->u_.s;
              break;
          }
          queue_.updateReadIndex();
        }
        file_.flush();

        using namespace std::literals::chrono_literals;
        std::this_thread::sleep_for(10ms);
      }
    }

    /**
     * @brief 构造函数
     * @param file_name 日志文件名
     * @details 创建日志记录器，打开日志文件并启动后台写入线程
     */
    explicit OptLogger(const std::string &file_name)
        : file_name_(file_name), queue_(LOG_QUEUE_SIZE) {
      file_.open(file_name);
      ASSERT(file_.is_open(), "Could not open log file:" + file_name);
      logger_thread_ = Common::createAndStartThread(-1, "Common/OptLogger " + file_name_, [this]() { flushQueue(); });
      ASSERT(logger_thread_ != nullptr, "Failed to start OptLogger thread.");
    }

    /**
     * @brief 析构函数
     * @details 关闭日志记录器，等待队列清空，停止后台线程并关闭文件
     */
    ~OptLogger() {
      std::string time_str;
      std::cerr << Common::getCurrentTimeStr(&time_str) << " Flushing and closing OptLogger for " << file_name_ << std::endl;

      while (queue_.size()) {
        using namespace std::literals::chrono_literals;
        std::this_thread::sleep_for(1s);
      }
      running_ = false;
      logger_thread_->join();

      file_.close();
      std::cerr << Common::getCurrentTimeStr(&time_str) << " OptLogger for " << file_name_ << " exiting." << std::endl;
    }

    /**
     * @brief 将不同类型的日志条目写入无锁队列
     * @details 创建正确类型的LogElement并将其写入无锁队列
     */
    /**
     * @brief 将日志元素推入队列
     * @param log_element 要推入的日志元素
     * @details 将日志元素写入无锁队列并更新写入索引
     */
    auto pushValue(const LogElement &log_element) noexcept {
      *(queue_.getNextToWriteTo()) = log_element;
      queue_.updateWriteIndex();
    }

    /**
     * @brief 推入字符类型日志
     * @param value 要记录的字符
     */
    auto pushValue(const char value) noexcept {
      pushValue(LogElement{LogType::CHAR, {.c = value}});
    }

    /**
     * @brief 推入整数类型日志
     * @param value 要记录的整数
     */
    auto pushValue(const int value) noexcept {
      pushValue(LogElement{LogType::INTEGER, {.i = value}});
    }

    /**
     * @brief 推入长整数类型日志
     * @param value 要记录的长整数
     */
    auto pushValue(const long value) noexcept {
      pushValue(LogElement{LogType::LONG_INTEGER, {.l = value}});
    }

    /**
     * @brief 推入长长整数类型日志
     * @param value 要记录的长长整数
     */
    auto pushValue(const long long value) noexcept {
      pushValue(LogElement{LogType::LONG_LONG_INTEGER, {.ll = value}});
    }

    /**
     * @brief 推入无符号整数类型日志
     * @param value 要记录的无符号整数
     */
    auto pushValue(const unsigned value) noexcept {
      pushValue(LogElement{LogType::UNSIGNED_INTEGER, {.u = value}});
    }

    /**
     * @brief 推入无符号长整数类型日志
     * @param value 要记录的无符号长整数
     */
    auto pushValue(const unsigned long value) noexcept {
      pushValue(LogElement{LogType::UNSIGNED_LONG_INTEGER, {.ul = value}});
    }

    /**
     * @brief 推入无符号长长整数类型日志
     * @param value 要记录的无符号长长整数
     */
    auto pushValue(const unsigned long long value) noexcept {
      pushValue(LogElement{LogType::UNSIGNED_LONG_LONG_INTEGER, {.ull = value}});
    }

    /**
     * @brief 推入浮点数类型日志
     * @param value 要记录的浮点数
     */
    auto pushValue(const float value) noexcept {
      pushValue(LogElement{LogType::FLOAT, {.f = value}});
    }

    /**
     * @brief 推入双精度浮点数类型日志
     * @param value 要记录的双精度浮点数
     */
    auto pushValue(const double value) noexcept {
      pushValue(LogElement{LogType::DOUBLE, {.d = value}});
    }

    /**
     * @brief 推入C风格字符串日志
     * @param value 要记录的C风格字符串
     * @details 直接将字符串复制到日志元素中，比Logger类更高效
     */
    auto pushValue(const char *value) noexcept {
      LogElement l{LogType::STRING, {.s = {}}};
      strncpy(l.u_.s, value, sizeof(l.u_.s) - 1);
      pushValue(l);
    }

    /**
     * @brief 推入标准字符串日志
     * @param value 要记录的标准字符串
     */
    auto pushValue(const std::string &value) noexcept {
      pushValue(value.c_str());
    }

    /**
     * @brief 格式化日志并写入日志队列
     * @tparam T 第一个参数类型
     * @tparam A 其余参数类型包
     * @param s 格式化字符串，使用%作为占位符
     * @param value 第一个要替换的值
     * @param args 其余要替换的值
     * @details 解析格式化字符串，将%替换为传入的参数值，并将结果写入无锁队列
     */
    template<typename T, typename... A>
    auto log(const char *s, const T &value, A... args) noexcept {
      while (*s) {
        if (*s == '%') {
          if (UNLIKELY(*(s + 1) == '%')) { // to allow %% -> % escape character.
            ++s;
          } else {
            pushValue(value); // substitute % with the value specified in the arguments.
            log(s + 1, args...); // pop an argument and call self recursively.
            return;
          }
        }
        pushValue(*s++);
      }
      FATAL("extra arguments provided to log()");
    }

    /**
     * @brief 无需替换的格式化日志函数重载
     * @param s 要记录的纯文本字符串
     * @details 处理不需要参数替换的情况，注意这是函数重载而非模板特化，gcc不允许内联特化
     */
    auto log(const char *s) noexcept {
      while (*s) {
        if (*s == '%') {
          if (UNLIKELY(*(s + 1) == '%')) { // to allow %% -> % escape character.
            ++s;
          } else {
            FATAL("missing arguments to log()");
          }
        }
        pushValue(*s++);
      }
    }

    /**
     * @brief 删除的默认、拷贝和移动构造函数及赋值运算符
     * @details 防止意外的日志记录器复制或移动，确保日志记录器的唯一性
     */
    OptLogger() = delete;

    OptLogger(const OptLogger &) = delete;

    OptLogger(const OptLogger &&) = delete;

    OptLogger &operator=(const OptLogger &) = delete;

    OptLogger &operator=(const OptLogger &&) = delete;

  private:
    /**
     * @brief 日志文件相关成员
     */
    const std::string file_name_;
    std::ofstream file_;

    /**
     * @brief 日志元素的无锁队列
     * @details 用于从主日志线程到后台格式化和磁盘写入线程的数据传输
     */
    Common::LFQueue<LogElement> queue_;
    std::atomic<bool> running_ = {true};

    /**
     * @brief 后台日志线程
     * @details 负责将日志队列中的数据写入文件
     */
    std::thread *logger_thread_ = nullptr;
  };
}
